#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include "send_error_page.h"
#include "write_buffer.h"
#include "end_of_request.h"
#include "http_server.h"
#include "version.h"

static char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

int send_error_page (http_connection_t *http_connection, int error_code, host_t *host)
{
	static char http_error_page[] =
		"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">"
		"<html>"
			"<head>"
				"<title>%s</title>"
			"</head>"
			"<body>"
				"<h1>"
					"HTTP/1.1 %d %s"
				"</h1>"
			"</body>"
		"</html>";

	struct tm *stm;
	buffer_t *buffer;
	buffer_t *message_body;
	const char *message;
	const char *connection_header;

#ifdef HAVE_TCP_CORK
	int optval;
#endif /* HAVE_TCP_CORK */

	int ret;

	switch (error_code) {
		case 301:
			message = "Moved Permanently";
			break;
		case 304:
			message = "Not Modified";
			break;
		case 400:
			http_connection->keep_alive = 0;

			message = "Bad Request";
			break;
		case 401:
			message = "Unauthorized";
			break;
		case 403:
			message = "Forbidden";
			break;
		case 404:
			message = "Not Found";
			break;
		case 411:
			message = "Length Required";
			break;
		case 413:
			message = "Request Entity Too Large";
			break;
		case 414:
			message = "Request-URI Too Long";
			break;
		case 416:
			message = "Requested Range Not Satisfiable";
			break;
		case 500:
			message = "Internal Server Error";
			break;
		case 501:
			message = "Not Implemented";
			break;
		case 502:
			message = "Bad Gateway";
			break;
		case 505:
			message = "HTTP Version Not Supported";
			break;
		default:
			return -1;
	}

	message_body = &http_connection->message_body;
	if (buffer_format (message_body, http_error_page, message, error_code, message) < 0) {
		return -1;
	}

	buffer = &http_connection->buffer;
	buffer_reset (buffer);

	if (buffer_format (buffer, "HTTP/1.1 %d %s\r\n", error_code, message) < 0) {
		return -1;
	}

	if (error_code == 301) {
#define LOCATION_WITHOUT_PORT "Location: http://%s%s/\r\nContent-Type: text/html; charset=UTF-8\r\n"
#define LOCATION_WITH_PORT "Location: http://%s:%d%s/\r\nContent-Type: text/html; charset=UTF-8\r\n"
		if (http_server.port == 80) {
			if (buffer_format (buffer, LOCATION_WITHOUT_PORT, host->host_name, http_connection->path) < 0) {
				return -1;
			}
		} else {
			if (buffer_format (buffer, LOCATION_WITH_PORT, host->host_name, http_server.port, http_connection->path) < 0) {
				return -1;
			}
		}
	} else if (error_code == 304) {
#define LAST_MODIFIED "Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\nContent-Type: text/html; charset=UTF-8\r\n"
		stm = &http_connection->fmtime;
		if (buffer_format (buffer, LAST_MODIFIED, days[stm->tm_wday], stm->tm_mday, months[stm->tm_mon], 1900 + stm->tm_year, stm->tm_hour, stm->tm_min, stm->tm_sec) < 0) {
			return -1;
		}
	} else if (error_code == 401) {
#define ERROR_401_HEADER "WWW-Authenticate: Basic realm=\"Authorization Required\"\r\nContent-Type: text/html; charset=UTF-8\r\n"
		if (buffer_append_size_bounded_string (buffer, ERROR_401_HEADER, sizeof (ERROR_401_HEADER) - 1) < 0) {
			return -1;
		}
	} else if (error_code == 416) {
#define ERROR_416_HEADER "Content-Type: text/html; charset=UTF-8\r\nContent-Range: bytes 0-%ld/%ld\r\n"
		if (buffer_format (buffer, ERROR_416_HEADER, http_connection->filesize - 1, http_connection->filesize) < 0) {
			return -1;
		}
	} else {
#define ERROR_HEADER "Content-Type: text/html; charset=UTF-8\r\n"
		if (buffer_append_size_bounded_string (buffer, ERROR_HEADER, sizeof (ERROR_HEADER) - 1) < 0) {
			return -1;
		}
	}

	if (!http_connection->keep_alive) {
		connection_header = "close";
	} else {
		connection_header = "Keep-Alive";
	}

	stm = &http_server.stm;

	if (buffer_format (buffer, "Cache-Control: no-cache\r\n"
				   "Content-Length: %d\r\n"
				   "Connection: %s\r\n"
				   "Date: %s, %02d %s %d %02d:%02d:%02d GMT\r\n"
				   "Server: %s\r\n"
				   "\r\n", message_body->used, connection_header, days[stm->tm_wday], stm->tm_mday, months[stm->tm_mon], 1900 + stm->tm_year, stm->tm_hour, stm->tm_min, stm->tm_sec, WEBSERVER_NAME) < 0) {
		return -1;
	}

#ifdef HAVE_TCP_CORK
	if (http_connection->http_method != HEAD_METHOD) {
		optval = 1;
		setsockopt (http_connection->sd, IPPROTO_TCP, TCP_CORK, &optval, sizeof (int));
	}
#endif /* HAVE_TCP_CORK */

	http_connection->http_connection_state = WRITING_MESSAGE_HEADERS;
	http_connection->response_type = ERROR_PAGE_RESPONSE;
	http_connection->offset = 0;

	/* Write response header. */
	if ((ret = write_buffer (http_connection, buffer)) <= 0) {
		return ret;
	}

	/* Head method? */
	if (http_connection->http_method == HEAD_METHOD) {
		return end_of_request (http_connection);
	}

	http_connection->http_connection_state = WRITING_ERROR_PAGE;
	http_connection->offset = 0;

	/* Write message body. */
	if ((ret = write_buffer (http_connection, message_body)) <= 0) {
		return ret;
	}

	return end_of_request (http_connection);
}
